//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_CONTEXT_H__
#define __ELASTOS_CONTEXT_H__

#define THREAD_STACK_ORDER         1

INLINE Thread *GetCurrentThread()
{
    Thread *pThread;

    ASM("movl   %%esp, %%eax;"
        "andl   %1, %%eax;"
        :"=a"(pThread) :"i"(~0u << (PAGE_SHIFT + THREAD_STACK_ORDER)));

    return pThread;
}

//
// ThreadHContext
//
#define SIZEOF_FPU_DATA     80

typedef struct FPUContext
{
    uint32_t    controlWord;
    uint32_t    statusWord;
    uint32_t    tagWord;
    uint32_t    instructionPointerOffset;
    uint32_t    instructionPointerSelector;
    uint32_t    operandPointerOffset;
    uint32_t    operandPointerSelector;
    byte_t      data[SIZEOF_FPU_DATA];
    uint32_t    status;
} FPUContext;

enum ThreadHContextFlag
{
    ThreadHContextFlag_FPUInited    = 0x01,
    ThreadHContextFlag_FPUUsed      = 0x02,
};

class ThreadHContext
{
public:
    void Initialize();

    void Save();
    void Restore();

    void RestoreFPU();

private:
    void SaveFPU();

private:
    uint_t      m_uFlags;
    FPUContext  m_fpuContext;

    virtaddr_t  m_vaPageFaultAddress;

    // TODO: io bitmap
};

INLINE void ThreadHContext::Initialize()
{
    m_uFlags = 0;
    m_vaPageFaultAddress = 0;
}

#define SWITCH_TO_THREAD0(pThread0) \
    do { \
        g_tss.esp0 = (uint32_t)(pThread0)->m_vaKernelStack; \
        ASM("movl   %0, %%esp;" \
            "movl   %%esp, %%ebp;" \
            "ret;" \
            : :"g"((pThread0)->m_vaKernelStackPtr)); \
    } while(0)

#define THREAD_CONTEXT_SWITCH(pPrevThread, pNextThread) \
    do { \
        pPrevThread->m_hcontext.Save(); \
        pNextThread->m_hcontext.Restore(); \
        g_tss.esp0 = (uint32_t)(pNextThread)->m_vaKernelStack; \
\
        ASM("pushl  %gs;" \
            "pushl  %fs;" \
            "pushl  %ebx;" \
            "pushl  %esi;" \
            "pushl  %edi;" \
            "pushl  %ebp;" \
            "pushl  $ThreadContextSwitch_Exit;"); \
\
        ASM("movl   %%esp, %0;" \
            "movl   %1, %%esp;" \
            :"=m"((pPrevThread)->m_vaKernelStackPtr) \
            :"a"((pNextThread)->m_vaKernelStackPtr)); \
\
        ASM("ret;"); \
\
        ASM("ThreadContextSwitch_Exit:" \
            "popl   %ebp;" \
            "popl   %edi;" \
            "popl   %esi;" \
            "popl   %ebx;" \
            "popl   %fs;" \
            "popl   %gs;"); \
    } while(0)

#define THREAD_CONTEXT_SWITCH_NO_RETURN(pNextThread) \
    do { \
        pNextThread->m_hcontext.Restore(); \
        g_tss.esp0 = (uint32_t)(pNextThread)->m_vaKernelStack; \
\
        ASM("movl   %0, %%esp;" \
            : :"a"((pNextThread)->m_vaKernelStackPtr)); \
\
        ASM("ret;"); \
    } while(0)

//
// ProcessHContext
//
enum BreakPointType
{
    BreakPointType_Exec         = 0x01,
    BreakPointType_Write        = 0x02,
    BreakPointType_ReadWrite    = 0x03,
};

#define NUMBEROF_DEBUGREGISTERS     8

typedef struct DebugContext
{
    uint32_t    dr0;
    uint32_t    dr1;
    uint32_t    dr2;
    uint32_t    dr3;
    uint32_t    dr4;
    uint32_t    dr5;
    uint32_t    dr6;
    uint32_t    dr7;
} DebugContext;

class ProcessHContext
{
public:
    void Initialize();

    void Activate();

    ECode SetBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);

    void SetDebugRegisters(DebugContext *pContext);

    void GetDebugRegisters(DebugContext *pContext);

private:
    void ActivateDebugRegisters();

private:
    uint32_t    m_debugRegisters[NUMBEROF_DEBUGREGISTERS];
};

INLINE void ProcessHContext::Initialize()
{
    m_debugRegisters[7] = 0;
}

INLINE void ProcessHContext::Activate()
{
    ActivateDebugRegisters();
}

#ifdef KCONFIG_GLOBAL_BREAKPOINT
EXTERN ECode SetGlobalBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);
#endif // KCONFIG_GLOBAL_BREAKPOINT

#endif //__ELASTOS_CONTEXT_H__
